/**
 * @create 2022-06-14
 * @desc compress
 */

const {declare} = require('@babel/helper-plugin-utils');

// 获取唯一key
const base54 = (function(){
    var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
    return function(num) {
        var ret = "";
        do {
            ret = DIGITS.charAt(num % 54) + ret;
            num = Math.floor(num / 54);
        } while (num > 0);
        return ret;
    };
})();

const mangle = declare((api, options, dirname) => {
    api.assertVersion(7);

    return {
        pre(file) {
            file.set('uid', 0);
        },
        visitor: {
            // https://github.com/babel/babel/blob/main/packages/babel-types/src/ast-types/generated/index.ts
            // 别名，Scopable：作用域
            Scopable (path,state) {
                let uid = state.file.get('uid');

                // 当前作用域内声明的所有变量
                const bindings = path.scope.bindings;
                // Object.entries(bindings).forEach(bindingItem => {
                //     const [key, binding] = bindingItem;
                //
                //     if(binding.mangled) return;
                //     binding.mangled = true; // 做标记
                //     const newName = path.scope.generateUid(base54(uid++));
                //
                //     // 将所有引用名称一次性修改
                //     binding.path.scope.rename(key, newName)
                // })
                // state.file.set('uid', uid);


                // 删除无用变量 DCE
                Object.entries(bindings).forEach(([key, binding]) => {
                    const parentPath = binding.path.parentPath;

                    //没有被引用
                    if (!binding.referenced) {

                        // isImportDefaultSpecifier: import a from 'a'
                        // isImportSpecifier: import {a,b} from 'a'
                        // if (binding.path.isImportDefaultSpecifier() || binding.path.isImportSpecifier()) {
                        //  // 发现可以直接通过 parent 判断
                        // }

                        if (parentPath.isImportDeclaration()) {
                            if (parentPath.__checked) return;
                            const specifiers = parentPath.get('specifiers');

                            const noReferenced = specifiers.filter(item => {
                                let importName = '';
                                if (item.isImportSpecifier()) {
                                    importName = item.toString();
                                } else if (item.isImportDefaultSpecifier()) {
                                    importName = item.get('local').toString();
                                }

                                // scope 当前作用域， === path.scope
                                return !item.scope.getBinding(importName).referenced;
                            })

                            parentPath.__checked = true;
                            // console.log(key, noReferenced)

                            if (specifiers.length === noReferenced.length) {
                                parentPath.remove();
                            } else {
                                noReferenced.forEach(item => item.remove())
                            }

                            return;
                        }

                        // 如果赋值语句是函数调用，不能删除整句
                        if (binding.path.get('init').isCallExpression()) {
                            const comments = binding.path.get('init').node.leadingComments;//拿到节点前的注释
                            if(comments && comments[0]) {
                                if (comments[0].value.includes('PURE')) {//有 PURE 注释就删除
                                    binding.path.remove();
                                    return;
                                }
                            }
                        }

                        // babel 提供了一个 path.scope.isPure 的 api，可以判断一些 AST 节点是否是纯的，
                        // 也就是是否是没有副作用的，可以判断各种 AST 是否可以放心的删除。
                        //如果是纯的，就直接删除，否则替换为右边部分
                        if (binding.path.node.init && !path.scope.isPure(binding.path.node.init)) {
                            binding.path.parentPath.replaceWith(api.types.expressionStatement(binding.path.node.init));
                        } else {
                            binding.path.remove();
                        }
                    }

                });
            },

            // 去掉 return 后面的语句
            // 1. var 和 函数不能删除，存在变量提升
            BlockStatement (path) {
                const statementPaths = path.get('body');
                let purge = false;

                for (let i = 0; i < statementPaths.length; i++) {

                    // CompletionStatement：break、continue、return、Throw
                    if (statementPaths[i].isCompletionStatement()) {
                        purge = true;
                        continue;
                    }

                    if (purge && !canExistAfterCompletion(statementPaths[i])) {
                        statementPaths[i].remove();
                    }
                }
            }
        }
    }
});

module.exports = mangle;

function canExistAfterCompletion(path) {
    return path.isFunctionDeclaration() || path.isVariableDeclaration({
        kind: "var"
    });
}